
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>

#include "led_opr.h"

#define MIN(a,b) (a<b?a:b)
#definr LED_NUM 2 //设备数


static ssize_t led_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset);
static ssize_t led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset);
static int led_drv_open (struct inode *node, struct file *file);
static int led_drv_close (struct inode *node, struct file *file);


/* 1. 确定主设备号                                                                 */
static int major = 0;
static struct class *led_class;
struct led_operations *p_led_opr;
	
/* 3. 实现对应的open/read/write等函数，填入file_operations结构体                   */
//自定义读（文件，来自用户空间的buf，读多大数据，）
static ssize_t led_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
	int err;
	printk("%s %s line %d\n",__FILE__,__FUNCTION__,__LINE__);	

	return 0;
}
// write(fd,&val,1)
static ssize_t led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
	int err;
	char status;
	struct inode *inode = file_inode(file);
	int minor = iminor(inode);

	printk("%s %s line %d\n",__FILE__,__FUNCTION__,__LINE__);
	err = copy_from_user(status,buf,1);//只能通过函数实现用户空间与kernel的buf读写	

	/* 根据次设备号和status控制LED */
	p_led_opr->ctl(minor,status);
	return 1;
}

static int led_drv_open (struct inode *node, struct file *file)
{
	int minor = iminor(inode);//获取次设备号
	printk("%s %s line %d\n",__FILE__,__FUNCTION__,__LINE__);

	/* 根据次设备号初始化LED */
	p_led_opr->init(minor);

	return 0;
}
static int led_drv_close (struct inode *node, struct file *file)
{
	printk("%s %s line %d\n",__FILE__,__FUNCTION__,__LINE__);
	return 0;
}


/* 4. 把file_operations结构体告诉内核：通过一个入口函数来注册驱动程序。                                */
/* 5. 注册驱动程序得有一个入口函数：安装驱动程序时，就会去调用这个入口函数 */
static int __init led_init(void) //入口函数
{	
	int err;
	int i;
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	//主设备号 = 注册函数（major(0)，名称，结构体指针）
	major = register_chrdev(0,"DKB_led",&led_drv);//注册函数     /* dev/led */

	//创建class
	led_class = class_create(THIS_MODULE, "DKB_led_class"); 
	err = PTR_ERR(led_class);
	if (IS_ERR(led_class)) {
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
		unregister_chrdev(major, "led");
		return -1;
	}
	//创建device
	for(i=0,i<LED_NUM;i++)
	{
		device_create(led_class, NULL, MKDEV(major, i), NULL, "DKB_led%d",i); /* /dev/DKB_ledi */
	}
	
	p_led_opr = get_board_led_opr();

	return 0;
}


/* 6. 有入口函数就应该有出口函数：卸载驱动程序时，就会去调用这个出口函数           */
static void __exit led_exit(void) //入口函数
{
	int i;
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	for(i=0,i<LED_NUM;i++)
	{
		device_destroy(led_class,MKDEV(major, i));//销毁device i
	}
	
	class_destroy(led_class);	//销毁class
	
	//注销函数（major（设备号），名称）
	unregister_chrdev(major,"DKB_led");//注销函数
	return 0;
}


/* 7. 其他完善：提供设备信息，自动创建设备节点                                     */
module_init(led_init);//把这个函数修饰成入口函数
module_exit(led_exit);//把这个函数修饰成出口函数
MODULE_LICENSE("GPL");//驱动遵守GPL协议



